#include <gfx/dx12/swapchain.h>
#include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
#include <gfx/dx12/core.h>

namespace primal
{
	namespace gfx
	{
		constexpr u64 Frame_1{ FrameCount - 1u };

		SwapChain::SwapChain(GLFWwindow* window)
		{
			sync_event = CreateEventA(nullptr, FALSE, FALSE, nullptr);

			int width, height;
			glfwGetWindowSize(window, &width, &height);
			_desc.init_unvsync(static_cast<u32>(width), static_cast<u32>(height), static_cast<UINT>(FrameCount));

			ComPtr<IDXGISwapChain1> temp_swapchain{};
			
			factory()->CreateSwapChainForHwnd
			(
				direct_queue().Get(),
				glfwGetWin32Window(window),
				&_desc,
				nullptr,
				nullptr,
				temp_swapchain.GetAddressOf()
			);

			DXASSERT(temp_swapchain.As(this));

			for (auto& item : this->frames)
			{
				rtv().allocator(item.rt_handle);
				//graphics->dsv->allocator(item.ds_handle);
			}

			this->_flag = DXGI_PRESENT_ALLOW_TEARING;
			this->resize(_desc.Width, _desc.Height);
		}

		void SwapChain::resize(u32 width, u32 height)
		{
			this->_viewport.Width = static_cast<float>(width);
			this->_viewport.Height = static_cast<float>(height);
			this->_scissor_rect.right = static_cast<long>(width);
			this->_scissor_rect.bottom = static_cast<long>(height);

			for (auto& item : this->frames)
			{
				item.rt.Reset();
				item.ds.Reset();
			}

			DXASSERT(this->ptr_->ResizeBuffers(static_cast<UINT>(FrameCount), width, height, _desc.Format, _desc.Flags));

			for (u64 i = 0; i < FrameCount; i++)
			{
				auto& frame{ this->frames[i] };
				DXASSERT(this->ptr_->GetBuffer(i, IID_PPV_ARGS(frame.rt.GetAddressOf())));
				device()->CreateRenderTargetView(frame.rt.Get(), nullptr, frame.rt_handle.cpu);

				/*DXGI_FORMAT dsf = DXGI_FORMAT_D32_FLOAT;
				primal::d3dx::resource_desc ds_desc{};
				ds_desc.as_texture_2d(width, height, dsf, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);

				D3D12_CLEAR_VALUE cv{};
				cv.DepthStencil.Depth = 1.0f;
				cv.DepthStencil.Stencil = 0;
				cv.Format = dsf;

				DXASSERT(graphics->GetDevice()->CreateCommittedResource(&primal::d3dx::heap_properties.default_heap, D3D12_HEAP_FLAG_NONE, &ds_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &cv, IIDPPV(frame.ds)));

				primal::d3dx::view_desc depth_view_desc{};
				graphics->GetDevice()->CreateDepthStencilView(frame.ds.Get(), depth_view_desc.as_dsv_texture_2d(dsf), frame.ds_handle.cpu);*/
			}

			this->current_index = 0ull;
		}

		void SwapChain::delayed_resize(u32 width, u32 height)
		{
			resize_parameter.width = width;
			resize_parameter.height= height;
			resize_parameter.is_resize = true;
		}

		void SwapChain::do_resize(DirectCMD* cmd)
		{
			if (resize_parameter.is_resize)
			{
				cmd->Flush();
				resize_parameter.is_resize = false;
				resize(resize_parameter.width, resize_parameter.height);
			}
		}

		void SwapChain::present()
		{
			DXASSERT(ptr_->Present1(this->_sync_interval, this->_flag, &this->_parameters));
		}

		void SwapChain::begin(ID3D12GraphicsCommandListN* list)
		{
			this->present();
			++this->current_index;
			this->current_index &= Frame_1;
			auto& cur_frame{ this->frames[this->current_index] };

			this->barrier.add(cur_frame.rt.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
			this->barrier.apply(list);

			timer += 0.002f;
			backcolor[0] = sinf(timer) / 2.0f + 0.5f;
			backcolor[1] = cosf(timer) / 2.0f + 0.5f;

			list->OMSetRenderTargets(1u, &cur_frame.rt_handle.cpu, FALSE, nullptr);
			list->ClearRenderTargetView(cur_frame.rt_handle.cpu, backcolor, 0u, nullptr);
			//list->ClearDepthStencilView(cur_frame.ds_handle.cpu, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

		}

		void SwapChain::end(ID3D12GraphicsCommandListN* list)
		{
			auto& cur_frame{ this->frames[this->current_index] };
			this->barrier.add(cur_frame.rt.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
			this->barrier.apply(list);
		}
	}
}